/**
 * Copyright (c) 2007, Blue Hole Software. All rights reserved.
 * Code licensed under the Apache 2.0 License:
 * http://www.apache.org/licenses/
 */
package org.bhf.service.view;

import com.google.inject.Inject;
import org.bhf.facilities.Project;
import org.bhf.facilities.ProjectInjectors;
import org.bhf.json.JSONWriter;
import org.bhf.service.Constants;
import org.bhf.service.ProcessView;
import org.bhf.service.ServiceGroup;
import org.bhf.service.URLRouter;
import org.bhf.service.View;
import org.bhf.service.ViewResolver;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URLEncoder;

/**
 * The <code>PageProcessView</code> requires that you also include the <code>View</code> annotation on your Service.
 */
public class PageProcessView implements ProcessView
{
    /** Attribute under which the service is stored in request */
    public static String PAGE = "service";

    @Inject
    private URLRouter   router;

    private Object      service;
    private Method      method;
    private boolean     redirect;

    /**
     * Render the Page view.
     *
     * @param page This <code>Page</code>. Must not be <code>null</code>.
     * @param method The target service method
     * @param result Result of the method invocation.
     */
    public PageProcessView( final Object page, final Method method, final Object result )
    {
        this.service    = page;
        this.method     = method;
        this.redirect   = (result instanceof Boolean) && ((Boolean)result);
    }

    /**
     * ProcessView contract. Override to save service state in the service cache. A <code>ViewResolver</code> must have
     * been bound to a Project Injector.
     *
     * @param request Servlet request.
     * @param response Serlvet reponse.
     * @param servletContext The servlet context for this application
     * @throws java.io.IOException ConversionError during JSON rendering
     * @exclude
     */
    public void processView( final HttpServletRequest request, final HttpServletResponse response, final ServletContext servletContext )
        throws IOException, ServletException, JspException
    {
        final ServiceGroup module = ServiceGroup.getServiceGroupForService( service.getClass() );

        if( module == null )
            throw new JspException( "No service group associated with Page class " + service.getClass().getName() );

        if( redirect )
        {
            final StringBuffer url = new StringBuffer(
                router.reverseRoute( service.getClass(), method.getName(), Project.getContextProject().getName() )
            );

            url.append( "?" );
            url.append( Constants.OVERLAY_STATE_PARAMETER );
            url.append( "=" );
            url.append( URLEncoder.encode( new JSONWriter().writeObject( service ), "UTF-8" ) );

            // Tell the browser
            response.sendRedirect( response.encodeRedirectURL( url.toString() ) );
        }
        else if( !response.isCommitted() )
        {
            request.setAttribute( PAGE, service );

            final String uri = ProjectInjectors.getProjectInjector().getInstance( ViewResolver.class ).resolveView(
                module.getViewBase(),
                View.Util.view( service.getClass(), method ),
                request
            );

            final RequestDispatcher dispatcher  = servletContext.getRequestDispatcher( uri );
            if( dispatcher == null )
                throw new RuntimeException( "Unable to get a request dispatcher for view at " + uri );

            dispatcher.forward( request, response );
        }
    }
}
